/*
 * $Id: TPostgres.prg,v 1.1 2006/09/15 00:30:00 riztan Exp $
 *
 * xHarbour Project source code:
 * PostgreSQL RDBMS low level (client api) interface code.
 *
 * Copyright 2003 Rodrigo Moreno rodrigo_moreno@yahoo.com
 * www - http://www.xharbour.org
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this software; see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
 * Boston, MA 02111-1307 USA (or visit the web site http://www.gnu.org/).
 *
 * As a special exception, the Harbour Project gives permission for
 * additional uses of the text contained in its release of Harbour.
 *
 * The exception is that, if you link the Harbour libraries with other
 * files to produce an executable, this does not by itself cause the
 * resulting executable to be covered by the GNU General Public License.
 * Your use of that executable is in no way restricted on account of
 * linking the Harbour library code into it.
 *
 * This exception does not however invalidate any other reasons why
 * the executable file might be covered by the GNU General Public License.
 *
 * This exception applies only to the code released by the Harbour
 * Project under the name Harbour.  If you copy code from other
 * Harbour Project or Free Software Foundation releases into a copy of
 * Harbour, as the General Public License permits, the exception does
 * not apply to the code that you add in this way.  To avoid misleading
 * anyone as to the status of such modified files, you must delete
 * this exception notice from them.
 *
 * If you write modifications of your own for Harbour, it is your choice
 * whether to permit this exception to apply to your modifications.
 * If you do not wish that, delete this exception notice.
 *
 * See doc/license.txt for licensing terms.
 *
 */

/*  Noviembre 2008. Riztan Gutierrez  riztan(at)gmail.com
 *
 *  Clase TPQQuery 
 *  + var nRows para indicar nro de lineas
 *  de un query (nRows). 
 *
 *  + array aData con el contenido devuelto por
 *  el query.
 *
 *
 *  Clase TPQserver. 
 *  * Metodo Struct().
 *    + parametro cTypeStruct, para definir el tipo de estructura a presentar.
 *      por defecto sera tipo "DBF" de lo contrario... "postgresql"
 *  + Metodo List_OIDs( cTable )  
 *    Lista los OID de las relaciones existentes 
 *    ( oid, relname, relnamespace, relkind )
 *  + Metodo GetTable_OID( cTable )
 *    Para Obtener el oid de una tabla especifica.
 *  + Metodo pg_get_serial_sequence( cTabla, cCampo )
 *    Para obtener arreglo con la(s) secuencia(s) relacionadas a un campo de 
 *    una tabla especifica.
 *  + Metodo pg_get_viewdef( cView )
 *    Para obtener contenido que define una Vista.
 *  + Metodo get_index_of_table( cTabla )
 *    Retorna el(los) indice(s) perteneciente(s) a la tabla cTabla. No forma parte de postgresql.
 *  + Metodo pg_get_indexdef( uIndex )
 *    Retorna la definicion del indice pasado. El parametro puede ser el OID del indice
 *    o el nombre del indice.
 *   
 */

#include "gclass.ch"
#include "common.ch"
#include "hbclass.ch"
#include "postgres.ch"
#include "gnomedb.ch"

/* conexiones */
#define  POSTGRESQL    0
#define  LIBGDA        1


CLASS TPYConnection
    DATA     pConn
    DATA     nType
    DATA     cType        INIT ""
    
    DATA     pConn
    DATA     lTrans
    DATA     lallCols     INIT .T.
    DATA     Schema       INIT 'public'
    DATA     OID          INIT 0
    DATA     lError       INIT .F.
    DATA     nError       INIT 0
    DATA     cError       INIT ''
    DATA     lTrace       INIT .F.
    DATA     pTrace   
   
    METHOD   New( nType, cDSN, cHost, cDatabase, cUser, cPass, cPort, Schema )
    METHOD   Destroy()            
    METHOD   Close()                 INLINE ::Destroy()
    METHOD   SetSchema( cSchema )
    METHOD   StartTransaction()
    METHOD   Commit()
    METHOD   Rollback()
    METHOD   Query( cQuery )
    METHOD   Execute( cQuery )       INLINE ::Query(cQuery)

    METHOD   NetErr()                INLINE ::lError
    METHOD   Error()                 INLINE ::cError

//    METHOD   TransactionStatus()     INLINE PQtransactionstatus(::pConn)
/*


*/    
    METHOD   TableExists( cTable )
/*    
    METHOD   GetTable_OID( cTable )  INLINE IIF(Empty(cTable),0,::List_OIDs( cTable )[1,1])
*/    
    METHOD   ListTables()
/*    
    METHOD   List_OIDs( cTable )
    METHOD   GetTable_Comment( cTable, nCol )  
    METHOD   TableStruct( cTable, cTypeStruct )   // cTypeStruct por defecto es 'DBF'
    METHOD   CreateTable( cTable, aStruct )
    METHOD   DeleteTable( cTable  )
    METHOD   TraceOn(cFile)
    METHOD   TraceOff()
    METHOD   SetVerbosity(num)    INLINE PQsetErrorVerbosity( ::pConn, iif( num >= 0 .and. num <= 2, num, 1 )  )

    METHOD   get_index_of_table( cTabla )    // No pertenece al grupo de funciones de postgresql

    METHOD   pg_get_indexdef( uIndex )                // Definicion compatible con postgresql
    METHOD   pg_get_serial_sequence( cTabla, cCampo ) // Definicion compatible con postgresql
    METHOD   pg_get_viewdef( cView )                  // Definicion compatible con postgresql
    //DESTRUCTOR Destroy
*/
    METHOD   DataSource()    INLINE IIF( ::nType = POSTGRESQL, "PostgreSQL (Native - libpq)",;
                                                               gda_connection_get_provider(::pConn) )
ENDCLASS


/*
   cType := oConector:GetAttribute( "type" )

   cHost := oConector:GetAttribute( "host"     )
   cDB   := oConector:GetAttribute( "database" )
   cUser := oConector:GetAttribute( "user"     )
   cPass := oConector:GetAttribute( "password" )
   cPort := oConector:GetAttribute( "port"     )
*/

METHOD New( nType, cDSN, cHost, cDB, cUser, cPass, cPort, Schema ) CLASS TPYConnection

    Local res, aError:={}, nPort
    
    DEFAULT nType := POSTGRESQL, cPort := "5432"
    
    nPort := Val(cPort)
    ::nType := nType
    
    If ::nType = POSTGRESQL
       
       ::pConn := PQconnect(cDB, cHost, cUser, cPass, nPort)
       
       If PQstatus(::pConn) != CONNECTION_OK
           ::lError := .T.
           ::cError := PQerrormessage(::pConn)           
       Else                
           If ! Empty(Schema) 
               ::SetSchema(Schema)
           Else        
               res := PQexec( ::pConn, 'SELECT current_schema()' )        
               If PQresultStatus(res) == PGRES_TUPLES_OK
                   ::Schema := PQgetvalue( res, 1, 1 )
               EndIf
               PQclear(res)
               res := PQexec(::pConn,"select oid from pg_namespace where nspname = "+;
                                   DataToSql(::Schema) )
               If PQresultStatus(res) == PGRES_TUPLES_OK
                   ::OID := PQgetvalue( res, 1, 1 )
               EndIf
           EndIf
           ::cType := "Native PostgreSQL"
       EndIf

    ElseIf ::nType = LIBGDA
    
       ::pConn := gda_client_open_connection( tpy_gda_client(),   ;
                                              cDSN, cUser, cPass, ;
                                              GDA_CONNECTION_OPTIONS_DONT_SHARE,;
                                              @aError )
                                              
       
       If !Empty( aError )  // Miramos si existe algun tipo de error
          ::lError := .T.
          ::nError := aError[1]
          ::cError := aError[2]
          // MsgStop( cValtoChar( aError[1] ) + aError[2] )
       Else
          ::cType := "Gnome Data Access"
       EndIf
       
    Else
       MsgStop( "CONEXION NO DEFINIDA" )
    EndIf

    
    
RETURN self


METHOD Destroy() CLASS TPYConnection
    // ::TraceOff()
    If ::nType = POSTGRESQL
       PQClose( ::pConn )
       Return .T.
    EndIf
    
    If ::nType = LIBGDA
       gda_connection_close( ::pConn )
       Return .T.
    EndIf 
RETURN nil



METHOD SetSchema( cSchema ) CLASS TPYConnection
    Local res
    Local result := .F.
    
    If ::nType = POSTGRESQL .AND. PQstatus(::pConn) == CONNECTION_OK 

        res := PQexec( ::pConn, 'SET search_path TO ' + cSchema )        
        result := (PQresultStatus(res) == PGRES_COMMAND_OK)

        If !result
           Return result
        EndIf

        PQclear(res)
        res := PQexec(::pConn,"SELECT oid FROM pg_namespace WHERE nspname = "+;
                            DataToSql(cSchema) )
        result := (PQresultStatus(res) == PGRES_TUPLES_OK)
        If !result
           Return result
        EndIf

        ::Schema := cSchema
        ::OID    := AllTrim(CStr(PQgetvalue( res, 1, 1 )))
        PQclear(res)
    
    ElseIf ::nType = LIBGDA
        MsgStop( "NO SOPORTADO" )
        ::lError := .T.
        ::nError := 0
        ::cError := "Proceso no soportado"
    EndIf        
RETURN result



METHOD StartTransaction(cCommand) CLASS TPYConnection
    Local res, lError:=.F., aError:={}
    
    DEFAULT cCommand := 'BEGIN'
    
    If ::nType = POSTGRESQL
    
       res    := PQexec( ::pConn, cCommand )        
       lError := PQresultstatus(res) != PGRES_COMMAND_OK

       if lError
           ::lError := .T.
           ::cError := PQresultErrormessage(res)       
       else
           ::lError := .F.
           ::cError := ''    
       endif
       PQclear(res)    
       
    ElseIf ::nType = LIBGDA
    
       lError :=  gda_connection_supports_feature(::pConn, ;
                                    GDA_CONNECTION_FEATURE_TRANSACTIONS )
       If lError
          lError := gda_connection_begin_transaction( ::pConn, cCommand, ;
                                    GDA_TRANSACTION_ISOLATION_UNKNOWN, @aError )       
          If lError
             ::lError := .T.
             ::nError := aError[1]
             ::cError := aError[2]
          EndIf
       Else

          ::lError := .T.
          ::cError := "NO SOPORTADO"
          MsgStop("DB NO SOPORTA TRANSACCIONES")
       
       EndIf
       
    EndIf

RETURN lError



METHOD Commit( cCommand ) CLASS TPYConnection

    Local res, lError:=.F., aError := {}
    
    Default cCommand := 'COMMIT'
    
    If ::nType = POSTGRESQL
       res    := PQexec( ::pConn, cCommand )    
       lError := PQresultstatus(res) != PGRES_COMMAND_OK
    
       if lError
           ::lError := .T.
           ::cError := PQresultErrormessage(res)       
       else
           ::lError := .F.
           ::cError := ''    
       endif
       PQclear(res)

    ElseIf ::nType = LIBGDA
    
       lError := gda_connection_commit_transaction( ::pConn, cCommand, @aError )

       If lError
          ::lError := .T.
          ::nError := aError[1]
          ::cError := aError[2]
       EndIf
    
    EndIf
    
RETURN lError

    

METHOD Rollback( cCommand ) CLASS TPYConnection

    Local res, lError:=.F., aError:={}
    
    Default cCommand := 'ROLLBACK'
    
    If ::nType = POSTGRESQL
    
       res    := PQexec( ::pConn, cCommand )    
       lError := PQresultstatus(res) != PGRES_COMMAND_OK

       if lError
           ::lError := .T.
           ::cError := PQresultErrormessage(res)       
       else
           ::lError := .F.
           ::cError := ''    
       endif
       PQclear(res)

    ElseIf ::nType = LIBGDA
       lError := gda_connection_rollback_transaction( ::pBD, cCommand, @aError )    

       If lError
          ::lError := .T.
          ::nError := aError[1]
          ::cError := aError[2]
       EndIf

    EndIf

RETURN lError


METHOD ListTables() CLASS TPYConnection
    Local result := {}
    Local cQuery
    Local res
    Local i
    Local pDataModel
    Local aError := {}
    
    IF ::nType = POSTGRESQL
    
       cQuery := "select table_name "
       cQuery += " from information_schema.tables "
       cQuery += " where table_schema = " + DataToSql(::Schema) + " and table_type = 'BASE TABLE' "
    
       res := PQexec( ::pConn, cQuery )
    
       if PQresultstatus(res) == PGRES_TUPLES_OK
           For i := 1 to PQlastrec(res)
               aadd( result, PQgetvalue( res, i, 1 ) )
           Next                    
           ::lError := .F.
           ::cError := ''            
       else
           ::lError := .T.
           ::cError := PQresultErrormessage(res)
       endif
    
       PQclear(res)
       
    ElseIf ::nType = LIBGDA
    
       pDataModel:= gda_connection_get_schema( ::pConn, GDA_CONNECTION_SCHEMA_TABLES,;
                                               GDA_CONNECTION_OPTIONS_NONE, @aError )
                                               
       result := ARRAY( gda_data_model_get_n_rows( pDataModel )  )
       
       If !Empty( aError )
          ::lError := .t.
          ::nError := aError[1]
          ::cError := aError[2]
       Else
          for i=1 to Len( result )
             result[i]:= gda_value_stringify(  ;
                             gda_data_model_get_value_at( pDataModel, 1 , i ) ;
                         ) 
          next i
       EndIf

    EndIf
RETURN result  


METHOD TableExists( cTable ) CLASS TPYConnection
    Local result := .F.
    Local cQuery
    Local res
    
    Local cSchema := ::Schema

    If ::nType = POSTGRESQL

       cQuery := "select table_name "
       cQuery += " from information_schema.tables "
       cQuery += " where table_type = 'BASE TABLE' and table_schema = " 
       cQuery += DataToSql(cSchema) + " and table_name = " + DataToSql(lower(cTable))

       res := PQexec( ::pConn, cQuery )
    
       if PQresultstatus(res) == PGRES_TUPLES_OK
           result := (PQlastrec(res) != 0)
           ::lError := .F.
           ::cError := ''    
       else
           ::lError := .T.
           ::cError := PQresultErrormessage(res)       
       endif
    
       PQclear(res)
       
    ElseIf ::nType = LIBGDA
    
       IF ASCAN( ::ListTables(), cTable ) != 0
          result := .T.
       Else
          ::lError := .T.
          ::nError := 0
          ::cError := ""
       ENDIF
       
    EndIf
RETURN result    



METHOD Query( cQuery ) CLASS TPYConnection

    Local oQuery

    oQuery := TPYQuery():New(::pConn, ::nType, cQuery, ::lallCols, ::Schema)

RETURN oQuery

    



/*  CLASE QUERY
 *
 */
 
CLASS TPYQuery  // FROM TPYConnection
    DATA     pQuery
    DATA     pConn
    DATA     pDataModel

    DATA     lBof
    DATA     lEof
    DATA     lClosed
    DATA     lallCols INIT .T.

    DATA     lError   INIT .F.
    DATA     cError   INIT ''

    DATA     nType
    DATA     cQuery
    DATA     nRecno
    DATA     nFields
    DATA     nLastrec
    DATA     nRows

    DATA     aData
    DATA     aStruct
    DATA     aKeys
    DATA     TableName
    DATA     Schema
    DATA     rows     INIT 0

    METHOD   New( pConn, nType, cQuery, lallCols, cSchema, res )
    METHOD   Destroy()          
    METHOD   Close()            INLINE ::Destroy()

    METHOD   Refresh() 
/*
    METHOD   Skip( nRecno )             
    METHOD   Fetch()            INLINE ::Skip()

    METHOD   Bof()              INLINE ::lBof
    METHOD   Eof()              INLINE ::lEof
    METHOD   RecNo()            INLINE ::nRecno
    METHOD   Lastrec()          INLINE ::nLastrec
    METHOD   Goto(nRecno)       

    METHOD   NetErr()           INLINE ::lError
    METHOD   Error()            INLINE ::cError    
*/
    METHOD   FCount()           INLINE ::nFields
/*    
    METHOD   FieldName( nField )
    METHOD   FieldPos( cField )
    METHOD   FieldLen( nField )
    METHOD   FieldDec( nField )
    METHOD   FieldType( nField )
    METHOD   Update( oRow )
    METHOD   Delete( oRow )
    METHOD   Append( oRow )
    METHOD   SetKey()

    METHOD   Changed(nField)    INLINE ::aRow[nField] != ::aOld[nField]
    METHOD   Blank()            INLINE ::GetBlankRow()

    METHOD   Struct()
    
    METHOD   FieldGet( nField, nRow )
    METHOD   GetRow( nRow )   
    METHOD   GetBlankRow()  
*/    
    //DESTRUCTOR Destroy
ENDCLASS


METHOD New( pConn, nType, cQuery, lallCols, cSchema, res ) CLASS TPYQuery
    ::pConn      := pConn
    ::nType      := nType
    ::lClosed    := .T.    
    ::cQuery     := cQuery
    ::lallCols   := lallCols
    ::Schema     := cSchema
    ::aData      := {}
    ::pDataModel := NIL

    If !ISNIL(res)
       ::pQuery := res
    EndIf
    
    ::Refresh(ISNIL(res))
    
RETURN self


METHOD Destroy() CLASS TPYQuery

    If ::nType = POSTGRESQL
    
       if ! ::lClosed  
           PQclear( ::pQuery )    
           ::lClosed := .T.
       endif        
       
    ElseIf ::nType = LIBGDA //.AND. !ISNIL(::pQuery)
    
       gda_object_destroy_check( ::pQuery )
    
    EndIf
    
RETURN .T.


METHOD Refresh(lQuery) CLASS TPYQuery
    Local res
    Local cTableCodes := ''
    Local cFieldCodes := ''
    Local aStruct := {}
    Local aTemp := {}
    Local i
    Local n
    Local temp
    Local cQuery
    Local cType, cPType, nType, nDec, nSize
    Local oRow, aRow, aOld, nCol
    
    Local pDict, aError:={}
    Local pDataModel, cCommand

    Default lQuery To .T.
    
    ::Destroy()

    ::lBof     := .F.
    ::lEof     := .F.
    ::lClosed  := .F.    
    ::nRecno   := 0
    ::nFields  := 0
    ::nLastrec := 0    
    ::nRows    := 0
    ::aStruct  := {}
    ::aData    := {}
    ::Rows     := 0

    If ::nType = POSTGRESQL

       if lQuery
           res := PQexec( ::pConn, ::cQuery )
       else
           res := ::pQuery
       endif

       if PQresultstatus(res) == PGRES_TUPLES_OK     
           // Get some information about metadata
           aTemp := PQmetadata(res)

           if ISARRAY(aTemp)                        
               For i := 1 to Len(aTemp)
                   cType  := aTemp[ i, 2 ]
                   cPType := aTemp[ i, 2 ]
                   nSize  := aTemp[ i, 4 ]
                   nDec   := aTemp[ i, 5 ]
  
                   if nSize == 0 .and. PQlastrec(res) >= 1
                       nSize := PQgetLength(res, 1, i)                
                   endif                    
                
                   if 'char' $ cType 
                       cType := 'C'
        
                   elseif 'text' $ cType                 
                       cType := 'M'
        
                   elseif 'boolean' $ cType
                       cType := 'L'
                       nSize := 1
        
                   elseif 'smallint' $ cType 
                       cType := 'N'
                       nSize := 5
                
                   elseif 'integer' $ cType .or. 'serial' $ cType 
                       cType := 'N'
                       nSize := 9
                
                   elseif 'bigint' $ cType .or. 'bigserial' $ cType
                       cType := 'N'
                       nSize := 19
                
                   elseif 'decimal' $ cType .or. 'numeric' $ cType
                       cType := 'N'
                    
                       // Postgres don't store ".", but .dbf does, it can cause data width problem
                       if ! Empty(nDec)
                           nSize++
                       endif                        

                       // Numeric/Decimal without scale/precision can genarete big values, so, i limit this to 10,5
                       if nDec > 100
                           nDec := 5
                       endif
                
                       if nSize > 100
                           nSize := 15
                       endif        
        
                   elseif 'real' $ cType .or. 'float4' $ cType 
                       cType := 'N'
                       nSize := 15
                       nDec  :=  4
                
                   elseif 'double precision' $ cType .or. 'float8' $ cType 
                       cType := 'N'
                       nSize := 19
                       nDec  := 9
                
                   elseif 'money' $ cType               
                       cType := 'N'
                       nSize := 10
                       nDec  := 2
                
                   elseif 'timestamp' $ cType               
                       cType := 'C'
                       nSize := 20
          
                   elseif 'date' $ cType               
                       cType := 'D'
                       nSize := 8
        
                   elseif 'time' $ cType               
                       cType := 'C'
                       nSize := 10
        
                   else
                       // Unsuported
                       cType := 'K'
                   endif               
                
                   aadd( aStruct, {aTemp[ i, 1 ], cType, nSize, nDec, ;
                                   aTemp[i, 5], aTemp[i, 6], cPType} )
               Next    
            
               ::nFields  := PQfcount(res)
               ::nLastrec := PQlastrec(res)
               ::nRows    := PQnTuples(res)

               ::aData    := ARRAY(::nRows,::nFields)
/*
               for i := 1 to ::nRows
                  aRow := ARRAY( ::nFields )
                  aOld := ARRAY( ::nFields )
                  For nCol := 1 to ::nFields
                      ::aData[ i, nCol ] := CStr(PQGetValue(res, i, nCol))
                  Next
               Next i
*/
               if ::nLastrec <> 0
                   ::nRecno := 1
               endif                
                
               ::aStruct := aStruct        
           endif
    
           ::lError := .F.
           ::cError := ''    
    
       elseif PQresultstatus(res) == PGRES_COMMAND_OK
           ::lError := .F.
           ::cError := ''    
           ::rows   := val(PQcmdTuples(res))
           ::nRows  := PQnTuples(res)
        
       else
           ::lError := .T.
           ::cError := PQresultErrormessage(res)               
       endif
       
       ::pQuery := res


    ElseIf ::nType = LIBGDA

       pDict := gda_dict_new()

       gda_dict_set_connection( pDict, ::pConn )

       gda_dict_get_database( pDict )

       gda_dict_update_dbms_meta_data( pDict, 0, NIL, @aError)
       IIF( !Empty( aError ),  View( aError ),  NIL ) // revisando...

       ::pQuery := gda_query_new( pDict )
    
       gda_query_set_sql_text( ::pQuery, ::cQuery , @aError )
       IIF( !Empty( aError ),  View( aError ),  NIL ) // revisando...
    
       cCommand    := gda_command_new(::cQuery, GDA_COMMAND_TYPE_SQL, GDA_COMMAND_OPTION_STOP_ON_ERRORS)
       ::pDataModel  := gda_connection_execute_select_command( ::pConn, cCommand, NIL, @aError )
       IIF( !Empty( aError ),  View( aError ),  NIL ) // revisando...

       //::pDataModel  := gda_data_model_query_new( ::pQuery )
       
       ::nRows     := gda_data_model_get_n_rows( ::pDataModel )
       ::nFields   := gda_data_model_get_n_columns( ::pDataModel )

       For i = 1 to ::nFields
          ? G_VALUE_HOLDS_STRING( gda_data_model_get_value_at( ::pDataModel, i , 1 ) )
//          G_VALUE_HOLDS_STRING(  )
       Next i



//   gda_connection_close (cnc)

    
    EndIf
    

RETURN ! ::lError





CLASS GBOXGRID FROM GBOX

      METHOD New(  )

ENDCLASS

METHOD New( pDataModel, lhomogeneous, nSpacing, lMode, oParent, lExpand, lFill, nPadding, lContainer, x, y,uLabelTab,;
           lSecond, lResize, lShrink, left_ta, right_ta, top_ta, bottom_ta, xOptions_ta, yOptions_ta,;
           cId, uGlade ) CLASS GBOXGRID

       DEFAULT lMode := .F.,;
               nSpacing := 0,;
               lHomogeneous := .F.
               
       IF pDataModel = NIL
          MsgStop( "No Model Defined","ERROR")
          Return NIL
       ENDIF

       IF cId == NIL
          ::pWidGet = gnome_db_grid_new( pDataModel)
       ELSE
          ::pWidget := glade_xml_get_widget( uGlade, cId )
          ::CheckGlade( cId )
       ENDIF
        
       ::Register()

       IF oParent != NIL
          if oParent:ClassName() = "GWINDOW" .OR. oParent:ClassName() = "GDIALOG" // Si es una window, debe de usar CONTAINER
             lContainer := .T.
          endif
          ::AddChild( oParent, lExpand, lFill, nPadding, lContainer, x, y,;
                      uLabelTab,, lSecond, lResize, lShrink,;
                      left_ta, right_ta, top_ta, bottom_ta , xOptions_ta, yOptions_ta )
       ENDIF

      ::Show()

RETURN Self


